/*
 * iobuffer.h
 *
 *  Created on: 2016-4-2
 *      Author: ZhangXiyin
 */

#ifndef EYAS_SOURCES_BUFFER_IOBUFFER_H_
#define EYAS_SOURCES_BUFFER_IOBUFFER_H_
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>

#include "basic/basic.h"
#include "gc/gc.h"
#include "conf/def.h"
#include "fixed_buffer.hpp"

namespace eyas {
namespace buffer {
class iobuffer_slices;
typedef boost::shared_ptr<iobuffer_slices> iobuffer_slices_ptr;
class iobuffer;

class iobuffer_slices : public fixed_buffer<1400>{
public:
	iobuffer_slices(){}
	virtual ~iobuffer_slices(){}
private:
	friend class iobuffer;
	iobuffer_slices_ptr next_;

public:
	void append(const iobuffer_slices_ptr& bufferPtr){
		if(nullptr == next_){
			next_ = bufferPtr;
		}else{
			next_->append(bufferPtr);
		}
	}
	void append(iobuffer_slices_ptr&& bufferPtr){
		if(nullptr == next_){
			next_ = boost::move(bufferPtr);
		}else{
			next_->append(boost::move(bufferPtr));
		}
	}
};

class iobuffer {
public:
	// 构造，自动生成缓冲区
	iobuffer();
	// 接管buffer的数据区
	iobuffer(iobuffer& buffer);
	iobuffer(const iobuffer& buffer);
	// 移动构造
	iobuffer(EYAS_BUFFER_RV_REF(iobuffer) buffer);
	// 析构
	virtual ~iobuffer();
private:
	iobuffer_slices_ptr bufferPtr_;
	iobuffer_slices_ptr bufferEndPtr_;
	//boost::recursive_mutex mutex_;
	int32_t length_;

protected:
	void free();
	virtual void init();

public:
	int32_t length() const{return this->length_;}
	int32_t idlelength() const{
		return static_cast<int32_t>(nullptr == bufferEndPtr_ ? 0 : bufferEndPtr_->idlelength());
	}
	void clear(){
		this->bufferPtr_.reset();
		this->bufferEndPtr_.reset();
		this->length_ = 0;
	}
	iobuffer_slices_ptr new_slices();

	iobuffer& push(const iobuffer& buffer){\
		if(nullptr == bufferPtr_){
			bufferPtr_ = buffer.bufferPtr_;
			bufferEndPtr_ = buffer.bufferEndPtr_;
		}else{
			bufferEndPtr_->next_ = buffer.bufferPtr_;
			bufferEndPtr_ = buffer.bufferEndPtr_;
		}
		this->length_ += buffer.length();
		return *this;
	}
	iobuffer& push(const iobuffer_slices_ptr& bufferPtr){
		if(nullptr == bufferPtr_){
			bufferPtr_ = bufferPtr;
		}else{
			if(nullptr == bufferEndPtr_){
				bufferPtr_->append(bufferPtr);
			}else{
				bufferEndPtr_->append(bufferPtr);
			}
		}
		bufferEndPtr_ = bufferPtr;
		length_ += bufferPtr->length();
		return *this;
	}
	iobuffer& push(iobuffer_slices_ptr&& bufferPtr){
		if(nullptr == bufferPtr_){
			bufferPtr_ = bufferPtr;
		}else{
			if(nullptr == bufferEndPtr_){
				bufferPtr_->append(boost::move(bufferPtr));
			}else{
				bufferEndPtr_->append(boost::move(bufferPtr));
			}
		}
		bufferEndPtr_ = bufferPtr;
		length_ += bufferPtr->length();
		return *this;
	}
	iobuffer_slices_ptr pop(){
		if(nullptr != bufferPtr_){
			if(bufferPtr_->length() > 0){
				if(nullptr != bufferPtr_->next_){
					iobuffer_slices_ptr tmp = bufferPtr_;
					bufferPtr_ = bufferPtr_->next_;
					length_ -= tmp->length();
					return tmp;
				}else{
					iobuffer_slices_ptr tmp = bufferPtr_;
					bufferPtr_.reset();
					bufferEndPtr_.reset();
					length_ -= tmp->length();
					return tmp;
				}
			}else{
				if(nullptr != bufferPtr_->next_){
					//iobuffer_slices_ptr tmp = buffer_.bufferPtr_->next_;
					bufferPtr_ = bufferPtr_->next_;
					return pop();
				}
				return iobuffer_slices_ptr();
			}
		}
		return iobuffer_slices_ptr();
	}
	iobuffer_slices_ptr get_write_buffer(){
		if(nullptr == this->bufferEndPtr_){
			return this->new_slices();
		}
		if(this->bufferEndPtr_->idlelength() <= 0){
			return this->new_slices();
		}
		return this->bufferEndPtr_;
	}
	void commit_write(int32_t length){
		this->length_ += length;
	}
	class writer{
	public:
		writer(writer& w)
		:buffer_(w.buffer_), default_write_return(*this), default_replace_reaturn(*this){
			//mutex_.lock();
		}
	private:
		friend class iobuffer;
		writer(iobuffer& buffer)
		:buffer_(buffer), default_write_return(*this), default_replace_reaturn(*this){
			// 上锁
			//mutex_.lock();
		}
		writer(const writer& w)
		:buffer_(w.buffer_), default_write_return(*this), default_replace_reaturn(*this){
			//mutex_.lock();
		}
		~writer(){
			//mutex_.unlock();
		}

		typedef writer& write_return;
		writer& default_write_return;
		typedef writer& replace_return;
		writer& default_replace_reaturn;
	private:
		iobuffer& buffer_;

	public:

		//explicit
		write_return write(const unsigned char* buf, int32_t length){
			if( length <= 0 )
				return *this;
			this->buffer_.length_ += length;
			//iobuffer_slices_ptr slicesPtr = buffer_.bufferEndPtr_;
			if(nullptr == buffer_.bufferEndPtr_){
				//buffer_.push(buffer_.new_slices());
				buffer_.new_slices();
			}
			int32_t idlelen = buffer_.bufferEndPtr_->idlelength();
			if(idlelen <= 0){
				//buffer_.push(buffer_.new_slices());
				buffer_.new_slices();
				idlelen = buffer_.bufferEndPtr_->idlelength();
			}
			do{
				if(idlelen >= length){
					idlelen = length;
					buffer_.bufferEndPtr_->write(buf, length);
					return *this;
				}
				if(buffer_.bufferEndPtr_->write(buf, idlelen)){
					buf += idlelen;
					length -= idlelen;
				}else{
					// error 100%不会发生
				}
//				buffer_.bufferEndPtr_->next_ = buffer_.new_slices();
//				buffer_.bufferEndPtr_ = buffer_.bufferEndPtr_->next_;
				buffer_.new_slices();
				idlelen = buffer_.bufferEndPtr_->idlelength();
			}while(length > 0);
			return *this;
		}

#include "impl/buffer_write.hpp"

		replace_return replace(int32_t offset, const unsigned char* buf, int32_t length){
			if( length <= 0 )
				return *this;
			if(nullptr == buffer_.bufferPtr_){
				return default_replace_reaturn;
			}
			iobuffer_slices_ptr slicesPtr = buffer_.bufferPtr_;
			int32_t slicesLen = slicesPtr->length();
			while(offset >= slicesLen){
				slicesPtr = slicesPtr->next_;
				offset -= slicesLen;
				if(nullptr == slicesPtr){
					return default_replace_reaturn;
				}
				slicesLen = slicesPtr->length();
			}
			do{
				if(length <= slicesLen){
					// 可写入完整
					slicesPtr->replace(offset, buf, length);
					break;
				}else{
					// 只可写入部分
					slicesPtr->replace(offset, buf, slicesLen);
					offset = 0;
					buf += slicesLen;
					length -= slicesLen;
					if(nullptr == slicesPtr->next_){
						return default_replace_reaturn;
					}
					slicesPtr = slicesPtr->next_;
					slicesLen = slicesPtr->length();
				}
			}while(true);
			return *this;
		}
#include "impl/buffer_replace.hpp"
	};
	class reader{
	public:
		reader(reader& w)
		:buffer_(w.buffer_){
			//mutex_.lock();
		}
	private:
		friend class iobuffer;
		reader(iobuffer& buffer)
		:buffer_(buffer){
			//mutex_.lock();
		}
		reader(const reader& w)
		:buffer_(w.buffer_){
			//mutex_.lock();
		}
		~reader(){
			//mutex_.unlock();
		}
	private:
		iobuffer& buffer_;
	
		int32_t length() const{return this->buffer_.length();}
	public:

		bool read(unsigned char* buf, int32_t length){
			if( length <= 0 )
				return true;
			if(nullptr == buffer_.bufferPtr_){
				return false;
			}
			this->buffer_.length_ -= length;
			int32_t slicesLen = buffer_.bufferPtr_->length();
			do{
				if(length <= slicesLen){
					// 可读取完整
					return buffer_.bufferPtr_->read(buf, length);
				}else{
					buffer_.bufferPtr_->read(buf, slicesLen);
					buf += slicesLen;
					length -= slicesLen;
				}
				if(nullptr == buffer_.bufferPtr_->next_){
					this->buffer_.length_ += length;
					return false;
				}
				buffer_.bufferPtr_ = buffer_.bufferPtr_->next_;
				slicesLen = buffer_.bufferPtr_->length();
			}while(true);
			return false;
		}
#include "impl/buffer_read.hpp"


		bool detect(int32_t offset, unsigned char* buf, int32_t length){
			if( length <= 0 )
				return false;
			if(nullptr == buffer_.bufferPtr_){
				return false;
			}
			iobuffer_slices* slicesPtr = buffer_.bufferPtr_.get();
			int32_t slicesLen = slicesPtr->length();
			while(offset >= slicesLen){
				slicesPtr = slicesPtr->next_.get();
				offset -= slicesLen;
				if(nullptr == slicesPtr){
					return false;
				}
				slicesLen = slicesPtr->length();
			}
			do{
				if(length <= slicesLen-offset){
					// 可探测完整
					slicesPtr->detect(offset, buf, length);
					break;
				}else{
					// 只可探测部分
					slicesPtr->detect(offset, buf, slicesLen);
					offset = 0;
					buf += slicesLen;
					length -= slicesLen;
					if(nullptr == slicesPtr->next_){
						return false;
					}
					slicesPtr = slicesPtr->next_.get();
					slicesLen = slicesPtr->length();
				}
			}while(true);
			return true;
		}
#include "impl/buffer_detect.hpp"
	};

private:
	// iobuffer内部不再进行互斥处理，遂可永久性实例化writer和reader
	writer writer_;
	reader reader_;
public:
	writer& get_writer();
	reader& get_reader();
};
inline iobuffer::writer& iobuffer::get_writer(){
	//return writer(*this);
	return writer_;
}
inline iobuffer::reader& iobuffer::get_reader(){
	//return reader(*this);
	return reader_;
}

inline iobuffer_slices_ptr iobuffer::new_slices(){
	this->push(eyas::gc::create_new<iobuffer_slices>());
	return bufferEndPtr_;
}

} /* namespace thread */
} /* namespace eyas */
#endif /* EYAS_SOURCES_BUFFER_IOBUFFER_H_ */
